package com.hqd.ch03.v51.boot.context.properties;

import com.hqd.ch03.v51.beans.factory.config.BeanDefinition;
import com.hqd.ch03.v51.beans.factory.support.BeanDefinitionBuilder;
import com.hqd.ch03.v51.boot.context.annotation.ImportBeanDefinitionRegistrar;
import com.hqd.ch03.v51.boot.context.properties.bind.validation.beanvalidation.MethodValidationExcludeFilter;
import com.hqd.ch03.v51.core.Conventions;
import com.hqd.ch03.v51.core.annotation.MergedAnnotation;
import com.hqd.ch03.v51.core.type.AnnotationMetadata;
import com.hqd.ch03.v51.registry.BeanDefinitionRegistry;

import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {

    private static final String METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME = Conventions
            .getQualifiedAttributeName(EnableConfigurationPropertiesRegistrar.class, "methodValidationExcludeFilter");

    static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
        ConfigurationPropertiesBindingPostProcessor.register(registry);
        BoundConfigurationProperties.register(registry);
    }

    static void registerMethodValidationExcludeFilter(BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME)) {
            BeanDefinition definition = BeanDefinitionBuilder
                    .genericBeanDefinition(MethodValidationExcludeFilter.class,
                            () -> MethodValidationExcludeFilter.byAnnotation(ConfigurationProperties.class))
                    .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition();
            registry.registerBeanDefinition(METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME, definition);
        }
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        registerInfrastructureBeans(registry);
        registerMethodValidationExcludeFilter(registry);
        ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
        getTypes(metadata).forEach(beanRegistrar::register);
    }

    private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
        return metadata.getAnnotations().stream(EnableConfigurationProperties.class)
                .flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE)))
                .filter((type) -> void.class != type).collect(Collectors.toSet());
    }

}
